/******************************************************************************
秋夜静，独坐对残灯。
啼笑非非谁识我，坐行梦梦尽缘君，何所慰消沉。
风卷雨，雨复卷侬心。
心似欲随风雨去，茫茫大海任浮沉。
无爱亦无恨。
******************************************************************************/
#include "kbRGB.h"
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

static hsv_t RandomHSVColor()
{
    hsv_t hsv;

    hsv.hue = rand() % 360;
    hsv.saturation = 255;
    hsv.value = 255;

    return hsv;
}
static uint32_t RandomRGBColor()
{
    hsv_t hsv = RandomHSVColor();
    return (hsv2rgb(&hsv));
}
static uint32_t Enlight(uint32_t color, float value)
{
    hsv_t hsv;
    rgb2hsv(color, &hsv);

    hsv.value *= value;

    return (hsv2rgb(&hsv));
}
static unsigned char InterpolateChanel(unsigned char a, unsigned char b, float x)
{
    return (int) ((b - a) * x + a);
}
#define RGBGetRValue(rgb)   (rgb & 0x000000FF)
#define RGBGetGValue(rgb)   ((rgb >> 8) & 0x000000FF)
#define RGBGetBValue(rgb)   ((rgb >> 16) & 0x000000FF)
#define ToRGBColor(r, g, b) ((uint32_t)((b << 16) | (g << 8) | (r)))
static uint32_t Interpolate(uint32_t color1, uint32_t color2, float fraction)
{
    return ToRGBColor(
                InterpolateChanel(RGBGetRValue(color1), RGBGetRValue(color2), fraction),
                InterpolateChanel(RGBGetGValue(color1), RGBGetGValue(color2), fraction),
                InterpolateChanel(RGBGetBValue(color1), RGBGetBValue(color2), fraction)
                );
}
/*************************************************************************************************\
|        Breathing [9]                                                                              |
\*************************************************************************************************/
void kbRGB_Breathing_Init(void)    // 呼吸
{
    kbRGB_Speed = 100;
    kbRGB_Slider2Val = 30;
}

void kbRGB_Breathing(const uint16_t cols, const uint16_t rows)    // 呼吸
{
     //kbRGB_progress += (( kbRGB_Speed / 100.0) / (float) kbRGB_FPS);
    kbRGB_progress += (( kbRGB_Speed / 100.0) / (float) kbRGB_FPS)*180.0f/KBRGB_PI;

     hsv_t CurrentColor;
    //if(kbRGB_progress >= KBRGB_PI) // PI
     if(kbRGB_progress >= 180) // PI
    {
        //kbRGB_progress -= KBRGB_PI;
         kbRGB_progress -= 180;

//        if(RandomColorsEnabled)
//        {
//           CurrentColor = ColorUtils::RandomHSVColor();
//        }
//        else
//        {
//           rgb2hsv(UserColors[0], &CurrentColor);
//        }
        CurrentColor = RandomHSVColor();
    }

    //CurrentColor.value = pow(sin(kbRGB_progress),3) * 255;
    double _sin = kbRGB_Sin(kbRGB_progress);
    _sin = _sin/(float)kbRGB_SinMax; CurrentColor.value = pow(_sin, 3) * 255;
    //printf("[%s--%d] Angle:%3d _sin:%f\r\n", __func__, __LINE__, (int32_t)(kbRGB_progress), _sin);
    //fflush(stdout);
    //for (ControllerZone* controller_zone: controller_zones)
//    {
//        controller_zone->SetAllZoneLEDs(hsv2rgb(&CurrentColor), Brightness);
//    }
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
           kbRGB_pset(col_id, row_id, hsv2rgb(&CurrentColor));
        }
    }
}

/*************************************************************************************************\
|        BreathingCircle [9]                                                                              |
\*************************************************************************************************/
void kbRGB_BreathingCircle_Init(void)    // 呼吸循环
{
    kbRGB_Speed = 20;
    kbRGB_Slider2Val = 3;
}
static uint32_t BreathingCircle_GetColor(const uint16_t x, const uint16_t y, const float w, const float h, const double progress)
{
    //float distance = std::min<float>(1.0, sqrt(pow(0.5 - x/w, 2) + pow(0.5 - y/h, 2)));
    float distance = sqrt(pow(0.5 - x/w, 2) + pow(0.5 - y/h, 2));
    if(distance>1.0f) distance=1.0f;
    distance = distance/1.5;
    hsv_t tmp;
    //tmp.hue = pow(sin(progress),3) * 255;
    tmp.hue = (int) (360 * (progress+distance) + 0) % 360;
    tmp.saturation =  255;
    tmp.value =  255;
//    if(distance > progress || distance < progress - kbRGB_Slider2Val/(0.5*(w+h)))
//        return 0x00;//ColorUtils::OFF();
    //tmp.value =  255*progress;

    //return UserColors[0];
    //return /*ColorUtils::*/RandomRGBColor();
    return hsv2rgb(&tmp);
}

void kbRGB_BreathingCircle(const uint16_t cols, const uint16_t rows)    // 呼吸循环
{
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = BreathingCircle_GetColor(col_id, row_id, cols-1, rows-1, kbRGB_progress);
            kbRGB_pset(col_id, row_id, color);
        }
    }
    kbrgb_Params.time += (float) kbRGB_Speed / (float) kbRGB_FPS*180.0f/KBRGB_PI;
    kbRGB_progress = 0.5 * (kbRGB_SinMax + kbRGB_Sin(0.1 * kbRGB_time))/(float)kbRGB_SinMax;
    //kbRGB_progress = sin(0.1 * kbrgb_Params.time);
}

/*************************************************************************************************\
|        Comet [9]                                                                              |
\*************************************************************************************************/
void kbRGB_Comet_Init(void)    // 彗星
{
    kbRGB_Speed = 25;
    kbRGB_Slider2Val = 50;
}
static uint32_t Comet_GetColor(const uint16_t i, const uint16_t width, const double progress, const double time)
{
    float comet_size = 0.01 * kbRGB_Slider2Val * width;

    float position = progress * 2 * width;
    hsv_t tmp;

    if(i > position)
    {
        return 0x00;//ColorUtils::OFF();
    }

    float distance = position - i;

    double value;

    if(distance > comet_size)
    {
        value = 0;
    }
    else if(distance == 0)
    {
        value = 1;
    }
    else
    {
        value = 1 - (distance / comet_size);
    }

    //if(RandomColorsEnabled)
    {
        tmp.hue = (int) (1000 * (time) + (distance / comet_size) * 360) % 360;
        tmp.saturation = pow(value, 0.2) * 255;
        tmp.value = pow(value, 3) * 255;
    }
//    else
//    {
//        hsv_t user_hsv;
//        rgb2hsv(UserColors[0], &user_hsv);

//        tmp.hue = user_hsv.hue;
//        tmp.saturation = pow(value, 0.2) * user_hsv.saturation;
//        tmp.value = pow(value, 3) * user_hsv.value;
//    }

    return (hsv2rgb(&tmp));
}
void kbRGB_Comet(const uint16_t cols, const uint16_t rows)    // 彗星
{
    int reverse = 0;
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Comet_GetColor(reverse ? cols - col_id - 1: col_id, cols, kbRGB_progress, kbRGB_time);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }
    kbrgb_Params.time += 0.01 * kbRGB_Speed / (float) kbRGB_FPS;
    double whole;
    kbRGB_progress = modf(kbRGB_time, &whole);
}

/*************************************************************************************************\
|        Fill [9]                                                                              |
\*************************************************************************************************/
//static double old_progress = 0;
//static uint32_t random;
#define old_progress kbrgb_Params.Simple_Fill_old_progress
void kbRGB_Fill_Init(void)    // 充满
{
    kbRGB_Speed = 50;
    kbRGB_Slider2Val = 30;
    old_progress = 0;
    kbrgb_Params.UserColors[0] = RandomRGBColor();
}
static uint32_t Fill_GetColor(const uint16_t idx, const uint16_t width, const uint32_t random)
{
    double position = kbRGB_progress * (width+3);

    double distance = position - idx;

    //uint32_t color = RandomColorsEnabled ? random : UserColors[0];
    uint32_t color = random;
    distance = distance/3;

    if(distance > 1)
    {
        return color;
    }

    if(distance > 0)
    {
        return Enlight(color, distance);
    }

    return 0x00;//ColorUtils::OFF();
}
void kbRGB_Fill(const uint16_t cols, const uint16_t rows)    // 充满
{
    int reverse = 0;
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Fill_GetColor(reverse ? cols - col_id - 1: col_id, cols, kbrgb_Params.UserColors[0]);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            //int LedID = controller_zones[i]->controller->zones[controller_zones[i]->zone_idx].matrix_map->map[((row_id * cols) + col_id)];
            //int LedID = (row_id * cols) + col_id;
            //controller_zone->SetLED(start_idx + LedID, color, Brightness);
            kbRGB_pset(col_id, row_id, color);
        }
    }

    kbRGB_time += 0.01 * kbRGB_Speed / (float) kbRGB_FPS;
    double whole;

    old_progress = kbRGB_progress;

    kbRGB_progress = modf(kbRGB_time, &whole);

    if(old_progress > kbRGB_progress/* && RandomColorsEnabled*/)
    {
        kbrgb_Params.UserColors[0] = /*ColorUtils::*/RandomRGBColor();
    }
}

/*************************************************************************************************\
|        Marquee [9]                                                                              |
\*************************************************************************************************/
void kbRGB_Marquee_Init(void)    // 大天幕
{
    kbRGB_Speed = 50;
    kbRGB_Slider2Val = 2;
    kbrgb_Params.hsv.saturation = 255;
    kbrgb_Params.hsv.value = 255;
    kbrgb_Params.hsv.hue = 0;
}
static uint32_t Marquee_GetColor(unsigned int i)
{
    unsigned int spacing = kbRGB_Slider2Val;

//    RandomColorsEnabled = true;
//    return (i + (int) progress) % spacing == 0 ?
//                RandomColorsEnabled ?
//                    RGBColor(hsv2rgb(&kbrgb_Params.hsv)) : UserColors[0]
//              : ColorUtils::OFF();
    return ((i + (int) kbRGB_progress) % spacing == 0) ? (hsv2rgb(&kbrgb_Params.hsv)) : 0x00;
}
void kbRGB_Marquee(const uint16_t cols, const uint16_t rows)    // 大天幕
{
    int reverse = 0;
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Marquee_GetColor(reverse ? cols - col_id - 1: col_id);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }
    kbRGB_progress += 0.1 * kbRGB_Speed / (float) kbRGB_FPS;

    //if(RandomColorsEnabled)
    {
        kbrgb_Params.hsv.hue++;
        kbrgb_Params.hsv.hue%=360;
    }
}

/*************************************************************************************************\
|        Mosaic [9]                                                                               |
\*************************************************************************************************/
//static struct kbRGB_Mosaic_t tiles[21*21];
#define  tiles kbrgb_Params.Simple.tiles
void kbRGB_Mosaic_Init(void)    // 马赛克
{
    kbRGB_Speed = 10;
    kbRGB_Slider2Val = 30;
    memset(tiles, 0x00, sizeof(tiles));
}
#if 0
static void Mosaic_UpdateTiles(void)
{
    unsigned int rarity = kbRGB_Slider2Val;
    struct Tile* tile=NULL;

    for(uint32_t i=0; i<(sizeof(tiles)/sizeof(tiles[0])); i++)
    {
        tile=&tiles[i];
        if(tile->brightness <= 0.f)
        {
            if(rand() % rarity == 0)
            {
                //tile->brightness = 1.f;
                tile->brightness = 255.f;
                tile->decrease_speed_mult = ((double) rand() / (RAND_MAX)) + 1;

//                if(RandomColorsEnabled)
//                {
//                    tile.hsv = ColorUtils::RandomHSVColor();
//                }
//                else
//                {
//                    rgb2hsv(UserColors[rand() % UserColors.size()], &tile.hsv);
//                }
                tile->hsv = /*ColorUtils::*/RandomHSVColor();
            }
        }

        //tile->brightness -= 0.0005 * kbRGB_Speed * tile->decrease_speed_mult;
        //tile->hsv.value = tile->brightness > 0 ?tile->brightness * 255 : 0;
        tile->brightness -= 0.0005*255 * kbRGB_Speed * tile->decrease_speed_mult;
        tile->hsv.value = tile->brightness > 0 ?tile->brightness : 0;
    }
}
#endif
// 为了节约内存选择牺牲算力
static uint32_t Mosaic_GetColor(const uint32_t old_rgb, struct kbRGB_Mosaic_t* const tile)
{
    unsigned int rarity = kbRGB_Slider2Val;
    hsv_t hsv;
    rgb2hsv(old_rgb, &hsv);
    if(tile->brightness <= 255)//if(tile->brightness <= 0.f)
    {
        if(rand() % rarity == 0)
        {
            //tile->brightness = 1.f;
            tile->brightness = 0xFFFF;//255.f;
            //tile->decrease_speed_mult = ((double) rand() / (RAND_MAX)) + 1;
            tile->decrease_speed_mult = (rand()&0xFF) + 255;
            hsv = RandomHSVColor();
        }
    }
//    tile->brightness -= 0.0005*255 * kbRGB_Speed * tile->decrease_speed_mult;
//    hsv.value = tile->brightness > 0 ?tile->brightness : 0;
    if(tile->brightness>=255) tile->brightness -= 0.0005*255 * kbRGB_Speed * tile->decrease_speed_mult;
    hsv.value = tile->brightness > 255 ?(tile->brightness>>8) : 0;
    return hsv2rgb(&hsv);
}
void kbRGB_Mosaic(const uint16_t cols, const uint16_t rows)    // 马赛克
{
    //Mosaic_UpdateTiles();
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
           uint16_t idx = (row_id * cols) + col_id;
           //uint32_t color = hsv2rgb(&tiles[idx].hsv);
           uint32_t color = Mosaic_GetColor(kbRGB_get_point(col_id, row_id), &tiles[idx]);
           kbRGB_pset(col_id, row_id, color);
        }
    }
}

/*************************************************************************************************\
|        MotionPoint [9]                                                                              |
\*************************************************************************************************/
//static uint32_t current=0x00;
#define current kbrgb_Params.UserColors[0]
void kbRGB_MotionPoint_Init(void)    // 运动点
{
    kbRGB_Speed = 25;
    kbRGB_Slider2Val = 30;
    current  = RandomRGBColor();
    kbRGB_progress = -90;
}
static uint32_t MotionPoint_GetColor(unsigned int w, const unsigned int x, const double t)
{
    if(w == 0)
    {
        w = 1;
    }

    float distance = fabs(x - t * (w-1));

    if(distance > 2)
    {
        return 0x00;//background;
    }

//    if(RandomColorsEnabled)
//    {
//        if(t <= 0.0005 || t >= 0.9995)
//        {
//            current  = ColorUtils::RandomRGBColor();
//        }
//    }
//    else
//    {
//        current = UserColors[0];
//    }
    if(t <= 0.0005 || t >= 0.9995)//if(t <= 0.0005 || t >= 0.9995)
    {
        current  = /*ColorUtils::*/RandomRGBColor();
    }

    return /*ColorUtils::*/Interpolate(current, /*background*/0x00, distance / 2.f);
}
void kbRGB_MotionPoint(const uint16_t cols, const uint16_t rows)    // 运动点
{
    //double t = (1 + sin(kbRGB_progress)) / 2.f;
    double t = (kbRGB_SinMax + kbRGB_Sin(kbRGB_progress)) / (2.f*kbRGB_SinMax);
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = MotionPoint_GetColor(cols, col_id, t);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }
    //kbRGB_progress += 0.1 * (float) kbRGB_Speed / (float) kbRGB_FPS;
    kbRGB_progress += (0.1*180/KBRGB_PI) * (float) kbRGB_Speed / (float) kbRGB_FPS;
    if(kbRGB_progress>=180)
    {
        kbRGB_progress -= 360;
    }
}

/*************************************************************************************************\
|        MotionPoints [9]                                                                              |
\*************************************************************************************************/
static void PointMove(struct kbRGB_MovingPoint* const point, float delta)
{
    switch(point->dir)
    {
        case LEFT:
        {
            point->x -= delta;
            break;
        }

        case RIGHT:
        {
            point->x += delta;
            break;
        }

        case UP:
        {
            point->y -= delta;
            break;
        }

        case DOWN:
        {
            point->y += delta;
            break;
        }
    }
}

static int PointIsOut(struct kbRGB_MovingPoint* const point)
{
    return point->x < 0.f || point->y < 0.f || point->x > 1.f || point->y > 1.f;
}

static void PointInit(struct kbRGB_MovingPoint* const point, uint32_t c)
{
    point->dir = rand() % 4;//static_cast<Dir>(rand() % 4);
    point->speed_mult = ((double) rand() / (RAND_MAX)) + 1;
    point->color = c;

    switch(point->dir)
    {
        case LEFT:
        {
            point->x = 1.f;
            point->y = ((double) rand() / (RAND_MAX));
            break;
        }

        case RIGHT:
        {
            point->x = 0.f;
            point->y = ((double) rand() / (RAND_MAX));
            break;
        }

        case UP:
        {
            point->x = ((double) rand() / (RAND_MAX));
            point->y = 1.f;
            break;
        }

        case DOWN:
        {
            point->x = ((double) rand() / (RAND_MAX));
            point->y = 0.f;
            break;
        }
    }
}

//static struct kbRGB_MovingPoint points[kbRGB_pointsSize];
//static uint16_t pointsSize=0;
static void MotionPoints_SetSlider2Val(unsigned int value)
{
    kbRGB_Slider2Val = value;
    for(unsigned int i = 0; i < value; i++)
    {
        PointInit(&kbrgb_Params.Simple.points[i], RandomRGBColor());
    }
}
void kbRGB_MotionPoints_Init(void)    // 运动点
{
    kbRGB_Speed = 50;
    kbRGB_Slider2Val = 4;
    MotionPoints_SetSlider2Val(kbRGB_pointsSize);
}
static void MotionPoints_UpdatePoints(const uint16_t pointsSize)
{
    for(uint16_t i=0; i<pointsSize; i++)//for(MovingPoint& point: points)
    {
        PointMove(&kbrgb_Params.Simple.points[i], 0.0001 * kbRGB_Speed * kbrgb_Params.Simple.points[i].speed_mult);

        if(PointIsOut(&kbrgb_Params.Simple.points[i]))
        {
            //point.Init(RandomColorsEnabled ? ColorUtils::RandomRGBColor() : UserColors[rand() % UserColors.size()]);
            PointInit(&kbrgb_Params.Simple.points[i], RandomRGBColor());
        }
    }
}

static uint32_t MotionPoints_GetColor(const float x, const float y, const float w, const float h)
{
    for(uint16_t i=0; i<kbRGB_pointsSize; i++)//for(MovingPoint& point: points)
    {
       float distance = sqrt(pow(kbrgb_Params.Simple.points[i].x * w - x, 2) + pow(kbrgb_Params.Simple.points[i].y * h - y, 2));
       if(distance <= 1)
       {
           return /*ColorUtils::*/Enlight(kbrgb_Params.Simple.points[i].color, 1 - distance);
       }
    }
    return 0x00;//ColorUtils::OFF();
}
void kbRGB_MotionPoints(const uint16_t cols, const uint16_t rows)    // 运动点
{
    MotionPoints_UpdatePoints(kbRGB_pointsSize);
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = MotionPoints_GetColor(col_id, row_id, cols, rows);
            kbRGB_pset(col_id, row_id, color);
        }
    }
}

/*************************************************************************************************\
|        Stack [9]                                                                              |
\*************************************************************************************************/
//static uint32_t current_colors[41];
//static unsigned int stops[41*41];
//static float progress[41*41];
//static unsigned int stops=0;
//static uint32_t zone_colors;
static uint32_t Stack_ResetZone(const uint16_t cols)
{
    kbrgb_Params.Simple.stops = cols-1;
//    kbrgb_Params.progress = 0.f;

//    for(unsigned int c = 0; c < 41; c++)
//    {
//        current_colors[c] = 0x00;//ColorUtils::OFF();
//    }

    uint32_t zone_colors = /*RandomColorsEnabled ? ColorUtils::*/RandomRGBColor();// : UserColors[0];
    return zone_colors;
}
void kbRGB_Stack_Init(void)    // 堆
{
    kbRGB_Speed = 30;
    kbRGB_Slider2Val = 30;
    kbrgb_Params.Simple.horizontal = 0;
    kbrgb_Params.UserColors[0] = Stack_ResetZone(21);
}
static uint32_t Stack_GetColor(const unsigned int led_idx, const unsigned int stops, const float zone_progress, const uint32_t zone_colors)
{
    unsigned int stop = stops;

    if(stop < led_idx)
    {
        return zone_colors;
    }

    //float zone_progress = kbRGB_progress;

    float distance = fabs(zone_progress - led_idx);

    if(distance > 1)
    {
        return 0x00;//ColorUtils::OFF();
    }

    return /*ColorUtils::*/Enlight(zone_colors, 1 - distance);
}
void kbRGB_Stack(const uint16_t cols, const uint16_t rows)    // 堆
{
    const int reverse=kbrgb_Params.reverse;
    // horizontal
    const int horizontal=kbrgb_Params.Simple.horizontal;
    if(0==horizontal)
    {
        for (uint16_t col_id = 0; col_id < cols; col_id++)
        {
            uint32_t color = Stack_GetColor(reverse ? cols - col_id - 1: col_id, kbrgb_Params.Simple.stops, kbRGB_progress, kbrgb_Params.UserColors[0]);
            //current_colors[col_id] = color;

            for (uint16_t row_id = 0; row_id < rows; row_id++)
            {
                kbRGB_pset(col_id, row_id, color);
            }
        }
    }
    // vertical
    else
    {
        for (uint32_t row_id = 0; row_id < rows; row_id++)
        {
            uint32_t color = Stack_GetColor(reverse ? rows - row_id - 1: row_id, kbrgb_Params.Simple.stops, kbRGB_progress, kbrgb_Params.UserColors[0]);
            //current_colors[row_id] = color;

            for (uint32_t col_id = 0; col_id < cols; col_id++)
            {
                kbRGB_pset(col_id, row_id, color);
            }
        }
    }
    float delta_progress = 0.1 * kbRGB_Speed / (float) kbRGB_FPS;

    kbRGB_progress += delta_progress * cols;//(sizeof(current_colors)/sizeof(current_colors[0]));

    if(kbRGB_progress >= kbrgb_Params.Simple.stops)
    {
        kbrgb_Params.Simple.stops--;
        if(kbrgb_Params.Simple.stops == 0)
        {
            if(0==horizontal) kbrgb_Params.UserColors[0] = Stack_ResetZone(cols);
            else kbrgb_Params.UserColors[0] = Stack_ResetZone(rows);
        }
        kbRGB_progress = 0.f;
    }
}

/*************************************************************************************************\
|        Swap [9]                                                                              |
\*************************************************************************************************/
//static uint32_t random1;
//static uint32_t random2;
#define random1 kbrgb_Params.UserColors[0]
#define random2 kbrgb_Params.UserColors[1]
//static uint32_t c1;
//static uint32_t c2;
//static uint32_t dir = 0;
//static uint32_t old_dir = 0;
void kbRGB_Swap_Init(void)    // 交换
{
    kbRGB_Speed = 10;
    kbRGB_Slider2Val = 30;
    kbrgb_Params.Simple.old_dir = 0;
    random1 = RandomRGBColor();
    random2 = RandomRGBColor();
}
static uint32_t Swap_GetColor(const unsigned int i, const unsigned int w, const double progress, const uint32_t dir, const uint32_t c1, const uint32_t c2)
{
    float x = dir ? progress : 1 - progress;
    return (i+1) <= x * (w+1) ? c1 : c2;
}
void kbRGB_Swap(const uint16_t cols, const uint16_t rows)    // 交换
{
    int reverse = 1;
    uint32_t c1;
    uint32_t c2;
    uint32_t dir = 0;
    double whole;
    uint32_t old_dir = kbrgb_Params.Simple.old_dir;
    c1 = random1;
    c2 = random2;
    //dir = (int) whole % 2;
    dir = ((int) kbRGB_time) % 2;
    double progress = modf(kbRGB_time, &whole);
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Swap_GetColor(reverse ? cols - col_id - 1: col_id, cols, progress, dir, c1, c2);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }
    kbRGB_time += 0.1 * kbRGB_Speed / (float) kbRGB_FPS;

//    float whole;

//    kbrgb_Params.progress = modf(kbrgb_Params.time, &whole);
//    //dir = (int) whole % 2;
//    dir = ((int) kbrgb_Params.time) % 2;

//    c1 = RandomColorsEnabled ? random1 : UserColors[0];
//    c2 = RandomColorsEnabled ? random2 : UserColors[1];

    if(old_dir == 0 && dir == 1)
    {
        random1 = RandomRGBColor();
    }
    else if(old_dir == 1 && dir == 0)
    {
        random2 = RandomRGBColor();
    }

    kbrgb_Params.Simple.old_dir = dir;
}
//static float  p = 0.;
//static float  p_step =0.;
//static int   step = 0;
//static int   last_step = 0;
//static float  width = 0.;
//static float  h_width = 0.;
/*************************************************************************************************\
|        Visor [9]                                                                              |
\*************************************************************************************************/
void kbRGB_Visor_Init(void)    // 遮阳板
{
    kbRGB_Speed = 50;
    kbRGB_Slider2Val = 20;
    random1 = RandomRGBColor();
    random2 = RandomRGBColor();
}
static uint32_t Visor_GetColor(const uint16_t i, float count, const int step, const float p_step, const float  width, const uint32_t c0, const uint32_t c1)
{
    // Constraint absolute minimum size
    //float w = std::max<float>(1.5/count, width);
    float w = 1.5/count;
    if(w<width) w=width;
    float x_step = p_step * (1.+ 4.*w) - 1.5*w;

    // dont divide by zero
    if(count <= 1)
    {
        count = 2;
    }

    float x = i / (count-1);
    float dist = x_step - x;


    // fade the head
    if(dist < 0)
    {
        //float l = std::max<float>(0.,std::min<float>((w+dist)/w, 1.));
        float l = (w+dist)/w;
        if(l>1.0) l=1.0;
        if(l<0) l=0;
        return step ? Enlight(c1, l) : Enlight(c0, l) ;
    }

    // fade the tail
    if(dist > w)
    {
        //float l = std::max<float>(0.,std::min<float>(1.-((dist-w)/w), 1.));
        float l = 1.-((dist-w)/w);
        if(l>1.0) l=1.0;
        if(l<0) l=0;
        return step ? Enlight(c0, l) : Enlight(c1, l) ;
    }

    // interpolate colors

    //float interp = std::min<float>(std::max<float>((w-dist)/w, 0.),1.);
    float interp = (w-dist)/w;
    if(interp<0) interp=0;
    if(interp>1) interp=1;

    return step ? Interpolate(c0,c1,interp) : Interpolate(c1,c0,interp);

}
void kbRGB_Visor(const uint16_t cols, const uint16_t rows)    // 遮阳板
{
    // Caculate a few things outside the loop
    kbRGB_progress += 0.01 * ((float)kbRGB_Speed) / ((float)kbRGB_FPS);

    float width = 0.01 * kbRGB_Slider2Val;          // [0-1] float, size of the visor
    //h_width = 0.5 * width;              // [0-0.5] float, half width
    float p = (kbRGB_progress - (long) kbRGB_progress);   // [0-1] float, 0 to 1 progress
    int step = p < 0.5;                     // p in [0-0.5] = fist step, p in [0.5-1] = second step
    float p_step =  step ? 2.*p : 2.*(1.- p); // [0-1] float, 0 to 1 progress within the step


//    int flipping = (last_step != step);

//    if(flipping) last_step = step;

    //if(flipping && RandomColorsEnabled)
    {
        random1 = RandomRGBColor();
        random2 = RandomRGBColor();
    }
//    else if(!RandomColorsEnabled)
//    {
//        c0 = UserColors[0];
//        c1 = UserColors[1];
//    }
    for (uint16_t col_id = 0; col_id < cols; col_id++)
    {
        uint32_t color = Visor_GetColor(col_id, cols, step, p_step, width, random1, random2);

        for (uint16_t row_id = 0; row_id < rows; row_id++)
        {
            kbRGB_pset(col_id, row_id, color);
        }
    }
}


